package com.train.mgr.modules.biz.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.train.common.model.PageResult;
import com.train.mgr.modules.biz.dto.TraineeCourseUserGroupVo;
import com.train.mgr.modules.biz.dto.request.StatQueryDTO;
import com.train.mgr.modules.biz.entity.*;
import com.train.mgr.modules.biz.mapper.StatCourseGroupMapper;
import com.train.mgr.modules.biz.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * 课程学员分组统计表 服务实现类
 * </p>
 *
 * @author cxg
 * @date 2025-10-11
 */
@Slf4j
@RequiredArgsConstructor
@Service("IStatCourseGroupService")
public class StatCourseGroupServiceImpl extends ServiceImpl<StatCourseGroupMapper, StatCourseGroup> implements IStatCourseGroupService {
    private final ICourseService courseService;
    private final IStatLessonGroupService statLessonGroupService;
    private final IStatLessonTraineeService statLessonTraineeService;
    private final ICourseUserService courseUserService;
    @Override
    public PageResult<StatCourseGroup> courseGroupList(StatQueryDTO params){
        Page<StatCourseGroup> page = params.buildPage();
        LambdaQueryWrapper<StatCourseGroup> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(StatCourseGroup::getCourseId,params.getCourseId());
        queryWrapper.eq(params.getGroupId()!=null && params.getGroupId() >0,StatCourseGroup::getGroupId,params.getGroupId());
        queryWrapper.orderByAsc(StatCourseGroup::getGroupCreateTime);
        Page<StatCourseGroup> resultPage = baseMapper.selectPage(page, queryWrapper);
        return PageResult.of(resultPage, StatCourseGroup.class);
    }

    @Override
    public void buildLessonTraineeStat(String courseId) {
        List<StatCourseGroup> stats = new ArrayList<>();
        UpdateWrapper<StatCourseGroup> updateWrapper = new UpdateWrapper<>();
        updateWrapper.ne(StrUtil.isBlank(courseId),"id", 0L);
        updateWrapper.eq(StrUtil.isNotBlank(courseId),"course_id", courseId);
        baseMapper.delete(updateWrapper);
        List<Course> courseList;
        if (StrUtil.isBlank(courseId)){
            courseList = courseService.getCourseList();
        } else {
            Course course = courseService.getById(courseId);
            courseList = Collections.singletonList(course);
        }
        for (Course course : courseList) {
            List<TraineeCourseUserGroupVo> userGroupList = courseUserService.getUserGroupList(course.getId());

            LambdaQueryWrapper<StatLessonTrainee> queryWrapper = new LambdaQueryWrapper<StatLessonTrainee>()
                    .eq(StatLessonTrainee::getCourseId, course.getId())
                    .eq(StatLessonTrainee::getIsDeleted, 0);
            List<StatLessonTrainee> lessonTraineeList = statLessonTraineeService.list(queryWrapper);
            Map<Long, List<StatLessonTrainee>> groupMap = lessonTraineeList.stream().filter(x -> x.getGroupId() != null && x.getGroupId() != 0).collect(Collectors.groupingBy(StatLessonTrainee::getGroupId));
            groupMap.forEach((groupId, statLessonGroups) -> {
                List<TraineeCourseUserGroupVo> collect = userGroupList.stream().filter(statLessonGroup -> statLessonGroup.getGroupId().equals(groupId)).collect(Collectors.toList());
                StatCourseGroup statCourseGroup = new StatCourseGroup();
                statCourseGroup.setCourseId(course.getId());
                statCourseGroup.setGroupId(groupId);
                statCourseGroup.setGroupName(collect.get(0).getGroupName());
                statCourseGroup.setCustomerId(course.getCustomerId());
                statCourseGroup.setGroupCreateTime(DateUtil.parse(collect.get(0).getGroupCreateTime()));
                statCourseGroup.setCreateTime(statLessonGroups.get(0).getCreateTime());
                statCourseGroup.setCreateBy(statLessonGroups.get(0).getCreateBy());
                statCourseGroup.setIsDeleted(false);

                Map<String, List<StatLessonTrainee>> userMap = statLessonGroups.stream().collect(Collectors.groupingBy(StatLessonTrainee::getUsername));
                statCourseGroup.setJoinNum(userMap.size());
                AtomicInteger finishNum = new AtomicInteger(0);
                final BigDecimal[] exercisesNum = {BigDecimal.ZERO};
                final BigDecimal[] exercisesStudentsNum = {BigDecimal.ZERO};
                final BigDecimal[] exercisesUseTime = {BigDecimal.ZERO};
                final BigDecimal[] readRepeatedTimes = {BigDecimal.ZERO};
                final BigDecimal[] readMaxScores = {BigDecimal.ZERO};
                final BigDecimal[] readAvgScores = {BigDecimal.ZERO};
                final BigDecimal[] readAvgRates = {BigDecimal.ZERO};
                AtomicInteger readAvgCounts =new AtomicInteger(0);
                final BigDecimal[] lessonOverallScores = {BigDecimal.ZERO};
                userMap.forEach((username, statLessonTrainees) -> {
                    List<StatLessonTrainee> statLessonTrainees1 = statLessonTrainees.stream().
                            filter(x -> x.getLessonFinishState() == null || x.getLessonFinishState().equals("0"))
                            .collect(Collectors.toList());
                    if (statLessonTrainees1.size() == 0) {
                        finishNum.getAndIncrement();
                    }
                    Integer reduce = statLessonTrainees.stream().map(StatLessonTrainee::getLessonExercisesNum).filter(Objects::nonNull).reduce(0, Integer::sum);
                    exercisesNum[0] = exercisesNum[0].add(new BigDecimal(reduce));
                    BigDecimal lessonLearnDuration = statLessonTrainees.stream().map(StatLessonTrainee::getLessonLearnDuration).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
                    exercisesUseTime[0] = exercisesUseTime[0].add(lessonLearnDuration);
                    Integer readRepeatedTime = statLessonTrainees.stream().map(StatLessonTrainee::getReadRepeatedTimes).filter(Objects::nonNull).reduce(0, Integer::sum);
                    readRepeatedTimes[0] = readRepeatedTimes[0].add(new BigDecimal(readRepeatedTime));
                    List<StatLessonTrainee> collect1 = statLessonTrainees.stream().filter(x -> x.getLessonExercisesNum() != null && x.getLessonExercisesNum() > 0).collect(Collectors.toList());
                    if (CollUtil.isNotEmpty(collect1)){
                        BigDecimal readAvgScore = BigDecimal.ZERO;
                        BigDecimal readAvgRate = BigDecimal.ZERO;
                        for (StatLessonTrainee statLessonTrainee : collect1) {
                            readAvgCounts.getAndAdd(statLessonTrainee.getLessonExercisesNum());
                            readAvgScore = readAvgScore.add(statLessonTrainee.getReadAvgScore().multiply(BigDecimal.valueOf(statLessonTrainee.getLessonExercisesNum())));
                            readAvgRate = readAvgRate.add(new BigDecimal(statLessonTrainee.getReadPassRate()).multiply(BigDecimal.valueOf(statLessonTrainee.getLessonExercisesNum())));
                        }
                        readAvgScores[0] = readAvgScores[0].add(readAvgScore);
                        readAvgRates[0] = readAvgRates[0].add(readAvgRate);
                        exercisesStudentsNum[0] = exercisesStudentsNum[0].add(BigDecimal.ONE);
                        BigDecimal readMaxScore = collect1.stream().map(StatLessonTrainee::getReadMaxScore).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
                        readMaxScores[0] = readMaxScores[0].add(readMaxScore.divide(BigDecimal.valueOf(collect1.size()), 2, RoundingMode.HALF_UP));
                        BigDecimal lessonOverallScore = collect1.stream().map(StatLessonTrainee::getLessonOverallScore).filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(collect1.size()), 2, RoundingMode.HALF_UP);
                        lessonOverallScores[0] = lessonOverallScores[0].add(lessonOverallScore);
                    }
                });

                statCourseGroup.setFinishNum(finishNum.get());
                statCourseGroup.setNoFinishNum(statCourseGroup.getJoinNum() - finishNum.get());
                BigDecimal finishB = new BigDecimal(finishNum.get());
                BigDecimal countB = BigDecimal.valueOf(statCourseGroup.getJoinNum());
                // 个人练习次数
                BigDecimal exercisesNums = exercisesNum[0];
                if (userMap.size() == 0 || exercisesNums.compareTo(BigDecimal.ZERO) == 0) {
                    statCourseGroup.setReadPassRate("0");
                    statCourseGroup.setPerExercisesNum(0);
                    statCourseGroup.setPerExercisesDuration(0);
                    statCourseGroup.setReadRepeatedTimes(0);
                    statCourseGroup.setReadAvgScore(BigDecimal.ZERO);
                    statCourseGroup.setOverallAvgScore(BigDecimal.ZERO);
                    statCourseGroup.setCourseFinishRate(BigDecimal.ZERO);
                } else {
                    statCourseGroup.setPerExercisesNum(exercisesNums.divide(BigDecimal.valueOf(userMap.size()), 1, RoundingMode.HALF_UP).intValue());
                    if (exercisesUseTime[0].compareTo(BigDecimal.ZERO) == 0) {
                        statCourseGroup.setPerExercisesDuration(0);
                    } else {
                        statCourseGroup.setPerExercisesDuration(exercisesUseTime[0].divide(BigDecimal.valueOf(userMap.size()), 1, RoundingMode.HALF_UP).intValue());
                    }
                    if (readRepeatedTimes[0].compareTo(BigDecimal.ZERO) == 0) {
                        statCourseGroup.setReadRepeatedTimes(0);
                    } else {
                        statCourseGroup.setReadRepeatedTimes(readRepeatedTimes[0].divide(BigDecimal.valueOf(userMap.size()), 1, RoundingMode.HALF_UP).intValue());
                    }
                    if (readAvgCounts.get() == 0){
                        statCourseGroup.setReadAvgScore(BigDecimal.ZERO);
                        statCourseGroup.setReadPassRate("0");
                    } else {
                        statCourseGroup.setReadAvgScore(readAvgScores[0].divide(BigDecimal.valueOf(readAvgCounts.get()), 1, RoundingMode.HALF_UP));
                        statCourseGroup.setReadPassRate(readAvgRates[0].divide(BigDecimal.valueOf(readAvgCounts.get()), 1, RoundingMode.HALF_UP).toPlainString());
                    }
                    BigDecimal finalReadPassRateTemp = BigDecimal.ZERO;
                    for (StatLessonTrainee statLessonGroup : statLessonGroups) {
                        String readPassRate = statLessonGroup.getReadPassRate();
                        if (StrUtil.isNotBlank(readPassRate)) {
                            readPassRate = readPassRate.replace("%", "");
                            finalReadPassRateTemp = finalReadPassRateTemp.add(new BigDecimal(readPassRate));
                        }
                    }
                    statCourseGroup.setOverallAvgScore(lessonOverallScores[0].divide(exercisesStudentsNum[0], 1, RoundingMode.HALF_UP));
                    statCourseGroup.setCourseFinishRate(finishB.multiply(BigDecimal.valueOf(100)).divide(countB, 1, RoundingMode.HALF_UP));
                    statCourseGroup.setReadMaxScore(readMaxScores[0].divide(exercisesStudentsNum[0], 1, RoundingMode.HALF_UP));
                }
               stats.add(statCourseGroup);
            });
        }
        if (CollUtil.isNotEmpty(stats)) {
            saveBatch(stats);
        }
    }

}
